unit uSimpleDivForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, uBaseDivForm, StdCtrls, ExtCtrls, ComCtrls;

type
  TCalcTarget = (ctVin,ctR1,ctR2,ctVout);

  TfmSimpleDiv = class(TfmBaseDivForm)
    imgR1: TImage;
    imgUIn: TImage;
    imgR2: TImage;
    imgUout: TImage;
    lvList: TListView;
    cbVariations: TCheckBox;
    btnCollect: TButton;
    edMin: TLabeledEdit;
    edMax: TLabeledEdit;
    laDivCurrent: TLabel;
    laDivCurrentUnit: TLabel;
    procedure imgUoutClick(Sender: TObject);
    procedure imgR1Click(Sender: TObject);
    procedure imgP2Click(Sender: TObject);
    procedure imgP3Click(Sender: TObject);
    procedure imgResClick(Sender: TObject);
    procedure imgR2Click(Sender: TObject);
    procedure btnCollectClick(Sender: TObject);
    procedure cbVariationsClick(Sender: TObject);
    procedure lvListCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure lvListDeletion(Sender: TObject; Item: TListItem);
    procedure DoFloatKeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
    FObjectIndex: TCalcTarget;
    FValues: array[0..3] of single;
  protected
    function DoCheckFields: boolean; override;
    procedure FieldsChanged; override;
  public
    { Public declarations }
    class function CalcFriendlyName: string; override;
    class function RunKey: string; override;
    class procedure InvokeCalc; override;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

var
  fmSimpleDiv: TfmSimpleDiv;

implementation

uses uFormReg, uENoms, uLocalization;

{$R *.dfm}

procedure TfmSimpleDiv.imgUoutClick(Sender: TObject);
begin
  inherited;
  LoadPictureFromRes(imgR1.Picture, 'R1');
  LoadPictureFromRes(imgR2.Picture, 'R2');
  LoadPictureFromRes(imgUin.Picture, 'UIN');
  LoadPictureFromRes(imgUout.Picture, 'UOUTS');
  SetLabels('P1', getString('#Global\Uin'),getString('#Global\Volt'));
  SetLabels('P2','R1', getString('#Global\Ohm'));
  SetLabels('P3','R2', getString('#Global\Ohm'));
  SetLabels('Result', getString('#Global\Uout'), ''(*getString('#Global\Volt')*));

  FObjectIndex := ctVout;
  SetLabels('Min',GetString('#global\min'), '');
  SetLabels('Max',GetString('#global\max'), '');
  laDivCurrent.Caption := GetLocaleString('idivmax');
  laDivCurrentUnit.Caption := GetString('#global\ma');

  cbVariations.Checked := false;
  cbVariations.Visible := false;
  cbVariationsClick(nil);
end;

procedure TfmSimpleDiv.imgR1Click(Sender: TObject);
begin
  inherited;
  LoadPictureFromRes(imgR1.Picture, 'R1S');
  LoadPictureFromRes(imgR2.Picture, 'R2');
  LoadPictureFromRes(imgUin.Picture, 'UIN');
  LoadPictureFromRes(imgUout.Picture, 'UOUT');
  SetLabels('P1', getString('#Global\Uin'), getString('#Global\Volt'));
  SetLabels('P2','R2', getString('#Global\Ohm'));
  SetLabels('P3', getString('#Global\Uout'), getString('#Global\Volt'));
  SetLabels('Result','R1', getString('#Global\Ohm'));
  FObjectIndex := ctR1;

  cbVariationsClick(nil);
  cbVariations.Visible := true;
end;

procedure TfmSimpleDiv.AfterConstruction;
begin
  inherited;
  ClientWidth := lvList.Left-2;
  edCurr.EditLabel.Caption := GetLocaleString('DivPower');
  cbVariations.Caption := GetLocaleString('findmatches');
  btnCollect.Caption := GetLocaleString('find');
  LocalizeListColumns(lvList);
  cbVariations.Caption := getString('#Global\findmatches');
  btnCollect.Caption := getString('#Global\find');
  Self.imgUoutClick(imgUout);
end;

function TfmSimpleDiv.DoCheckFields: boolean;
var tmp: single;
begin
  Result := false;
  if CheckField(edP1)<=0 then exit;

  case FObjectIndex of
    ctVout: begin
        Result := CheckErrorField(edP2,StrToValueEx(edP2.Text,0)>0) and CheckErrorField(edP3,StrToValueEx(edP3.Text,0)>0);
      end;
    ctR1,ctR2: begin
        if not cbVariations.Checked then
          Result := CheckErrorField(edP2,StrToValueEx(edP2.Text,0)>0) and CheckErrorField(edP3,StrToValueEx(edP3.Text,0)>0)
        else
          Result := checkFieldMore(edP3, 0);
      end;
  end;

  (*
  imgP2.Visible := false;
  if (edP2.Enabled) then begin
    tmp := CheckField(edP2);
    if tmp<=0 then exit;
  end;
  imgP2.Visible := (edP2.Enabled) and (tmp<>GetE24(tmp));

  tmp := CheckField(edP3);
  if tmp<=0 then exit;
  imgP3.Visible := (FObjectIndex=ctVout) and (tmp<>GetE24(tmp));
  *)

  //Result := true;
end;

procedure TfmSimpleDiv.FieldsChanged;
var tmp: single;
begin
  inherited;
  if (not edResult.Enabled) then exit;
  
  edResult.Text := GetString('#Global\InvalidData');
  edCurr.Text := GetString('#Global\NA');
  imgRes.Visible := false;

  FValues[0] := StrToFloat(edP1.Text);

  case FObjectIndex of
    // Vout = Vcc*(Rb/(Rt+rb))
    ctVout: begin
         FValues[1] := StrToValueEx(edP2.Text,1);
         FValues[2] := StrToValueEx(edP3.Text,1);

         if (FValues[1]=0) and (FValues[2]=0) then begin
           edResult.Text := GetString('#Global\ShortCircuit'); exit;
         end;
         if (FValues[1]=0) then FValues[3] := FValues[0]
         else
         if (FValues[2]=0) then FValues[3] := 0
         else
           FValues[3] := FValues[0]*(FValues[2]/(FValues[1]+FValues[2]));
       end;
    // Rtop = Vcc*Rbot/Vout
    ctR1: begin
         FValues[1] := StrToFloat(edP2.Text);
         FValues[2] := StrToFloat(edP3.Text);

         if (FValues[2]=0) or (FValues[2]>FValues[0]) then exit
         else
         FValues[3] := FValues[0]*FValues[1]/FValues[2];
         imgRes.Visible := FValues[3]<>GetE24(FValues[3]);
       end;
    // Rbot = (Vcc*Rtop)/(Vcc-Vout)-Rtop
    ctR2: begin
         FValues[1] := StrToFloat(edP2.Text);
         FValues[2] := StrToFloat(edP3.Text);
         
         FValues[3] := trunc((FValues[0]*FValues[1])/(FValues[0]-FValues[2])-FValues[1]);
       end;
  end;
  if (FObjectIndex=ctVout) then
    edResult.Text := formatValueV(FValues[3]) //Format('%.3f', [FValues[3]])
  else
    edResult.Text := Format('%.3f', [FValues[3]]);

  tmp := FValues[0]/(FValues[1]+FValues[2]);
  edCurr.Text := Format('%sA, %sW', [formatValue(tmp), formatValue(FValues[0]*tmp)]);
end;

procedure TfmSimpleDiv.imgP2Click(Sender: TObject);
begin
  inherited;
  edP2.Text := Format('%f', [GetE24(CheckField(edP2))]);
end;

procedure TfmSimpleDiv.imgP3Click(Sender: TObject);
begin
  inherited;
  edP3.Text := Format('%f', [GetE24(CheckField(edP3))]);
end;

procedure TfmSimpleDiv.imgResClick(Sender: TObject);
begin
  inherited;
  LockFields;
  try
    edResult.Text := Format('%f', [GetE24(CheckField(edResult))]);
    imgRes.Visible := false;
    case FObjectIndex of
      ctR1: begin
           // Recalc Vout
           edP3.Text := Format('%.3f', [CheckField(edP1)*(CheckField(edP2)/(CheckField(edResult)+CheckField(edP2)))]);
         end;
    end;
  finally UnlockFields; end;
end;

class function TfmSimpleDiv.CalcFriendlyName: string;
begin
  Result := 'Resistor voltage divider';
end;

class function TfmSimpleDiv.RunKey: string;
begin
  Result := 'resdiv';
end;

class procedure TfmSimpleDiv.InvokeCalc;
begin
  Application.CreateForm(TfmSimpleDiv, fmSimpleDiv);
  inherited;
end;

procedure TfmSimpleDiv.imgR2Click(Sender: TObject);
begin
  LoadPictureFromRes(imgR1.Picture, 'R1');
  LoadPictureFromRes(imgR2.Picture, 'R2S');
  LoadPictureFromRes(imgUin.Picture, 'UIN');
  LoadPictureFromRes(imgUout.Picture, 'UOUT');
  SetLabels('P1', getString('#Global\Uin'), getString('#Global\Volt'));
  SetLabels('P2','R1', getString('#Global\Ohm'));
  SetLabels('P3', getString('#Global\Uout'), getString('#Global\Volt'));
  SetLabels('Result','R2', getString('#Global\Ohm'));
  FObjectIndex := ctR2;

  cbVariationsClick(nil);
  cbVariations.Visible := true;
end;

procedure TfmSimpleDiv.btnCollectClick(Sender: TObject);
const mult: array[0..5] of single = (100,1e3,1e4,1e5,1e6,1e7);
var vin,vout,vavg,e1,e2,prc: single;
    idiv: array[0..1] of single;
    mxzero,m1,m2,a,b: integer;
begin
  vin := checkField(edP1);
  if (vin<=0) then exit;
  vout := checkField(edP3);
  if (vout<=0) then exit;

  idiv[0] := StrToFloatDef(edMin.Text,0);
  idiv[1] := StrToFloatDef(edMax.Text,0);

  btnCollect.Enabled := false;
  Application.ProcessMessages;

  lvList.Items.BeginUpdate;
  try
    lvList.Clear;
    mxzero := high(mult)-1;

    for m1 := low(mult) to mxzero do begin
      for a := Low(sfE24) to High(sfE24) do begin
        for m2 := low(mult) to mxzero do begin
          for b := Low(sfE24) to High(sfE24) do begin
            e1 := sfE24[a]*mult[m1];
            e2 := sfE24[b]*mult[m2];

            // Vout = Vcc*(Rb/(Rt+rb))
            vavg := vin*(e2/(e1+e2));

            prc := (vavg-vout)/vout*100;
            if (abs(prc) < 5) then begin
              // Check Idiv
              if ((idiv[1]<=0) or ((idiv[1]>0) and ((vin/(e1+e2))*1.0e3 <= idiv[1]))) and
                 ((idiv[0]<=0) or ((idiv[0]>0) and ((vin/(e1+e2))*1.0e3 >= idiv[0]))) then
                with lvList.Items.Add do begin
                  if (e2>e1) then begin
                    Caption := formatValue(e2);
                    SubItems.Add(formatValue(e1));
                  end else begin
                    Caption := formatValue(e1);
                    SubItems.Add(formatValue(e2));
                  end;
                  SubItems.Add(Format('%f', [vavg]));
                  SubItems.Add(Format('%f', [prc]));
                  SubItems.Add(formatValueVA(vin/(e1+e2))+'A');
                  Data := AllocMem(SizeOf(Single));
                  PSingle(Data)^ := prc;
                end;
              if lvList.Items.Count>500 then exit;
            end;
          end;
        end;
      end;
    end;

    lvList.AlphaSort;
    Self.RemoveListDupes(lvList);
  finally
    lvList.Items.EndUpdate;
    btnCollect.Enabled := true;
  end;
end;

procedure TfmSimpleDiv.cbVariationsClick(Sender: TObject);
begin
  if (cbVariations.Checked) then begin
    ClientWidth := lvList.Left+lvList.Width+6;
  end else begin
    case FObjectIndex of
      ctR1: SetLabels('P2','R2', getString('#Global\Ohm'));
      ctR2: SetLabels('P2','R1', getString('#Global\Ohm'));
    end;
    ClientWidth := lvList.Left-2;
  end;
  //SetLabels('P2', GetLocaleString('idivmax'), GetString('#global\ma'));
  Self.EnableSet('P2', not cbVariations.Checked);
  Self.EnableSet('Result', not cbVariations.Checked);
  Self.ShowSet('Result', not cbVariations.Checked);
  Self.EnableSet('Curr', not cbVariations.Checked);
  Self.ShowSet('Curr', not cbVariations.Checked);

  Self.EnableSet('Min', cbVariations.Checked);
  Self.ShowSet('Min', cbVariations.Checked);
  Self.EnableSet('Max', cbVariations.Checked);
  Self.ShowSet('Max', cbVariations.Checked);
  laDivCurrent.Visible := cbVariations.Checked;
  laDivCurrentUnit.Visible := cbVariations.Checked;
  
  btnCollect.Visible := cbVariations.Checked;
end;

procedure TfmSimpleDiv.lvListCompare(Sender: TObject; Item1, Item2: TListItem; Data: Integer; var Compare: Integer);
begin
  if abs(PSingle(Item1.Data)^) > abs(PSingle(Item2.Data)^) then Compare := 1
  else
  if abs(PSingle(Item1.Data)^) < abs(PSingle(Item2.Data)^) then Compare := -1
  else Compare := 0;
end;

procedure TfmSimpleDiv.lvListDeletion(Sender: TObject; Item: TListItem);
begin
  if (Item.Data<>nil) then FreeMem(Item.Data);
end;

procedure TfmSimpleDiv.BeforeDestruction;
begin
  lvList.Clear;
  inherited;
end;

procedure TfmSimpleDiv.DoFloatKeyPress(Sender: TObject; var Key: Char);
begin
  if (Sender=edP2) then begin
    //if ((FObjectIndex in [ctR1,ctR2]) then begin
    if not (cbVariations.Checked) then begin
      if (not CheckKeyPressFloatMult(Key,false)) then Key := #0;
      exit;
    end;
  end;

  if (Sender=edP3) then begin
    if (FObjectIndex in [ctVout]) then begin
      if (not CheckKeyPressFloatMult(Key,false)) then Key := #0;
      exit;
    end;
  end;

  inherited;
end;

initialization
  RegisterCalc(TfmSimpleDiv);

end.
